package com.imddy.gkrpc.client;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

import org.apache.commons.io.IOUtils;

import com.imddy.gkrpc.Request;
import com.imddy.gkrpc.Response;
import com.imddy.gkrpc.ServiceDescriptor;
import com.imddy.gkrpc.codec.Decoder;
import com.imddy.gkrpc.codec.Encoder;
import com.imddy.gkrpc.transport.TransportClient;

import lombok.extern.slf4j.Slf4j;

/**
 * 调用远程服务代理类
 * @author Administrator
 *
 */
@Slf4j
public class RemoteInvoker implements InvocationHandler {

	private Class clazz;
	private Encoder encoder;
	private Decoder decoder;
	private TransportSelector selector;
	
	public RemoteInvoker(Class clazz, Encoder encoder, Decoder decoder, TransportSelector selector) {
		this.clazz = clazz;
		this.decoder = decoder;
		this.encoder = encoder;
		this.selector = selector;
		
	}
	
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		Request request = new Request();
		request.setService(ServiceDescriptor.from(clazz, method));
		request.setParameters(args);
		
		Response response = invokerRemote(request);
		if (response == null || response.getCode() != 0) {
			throw new IllegalStateException("fail to invoke remote: " + response);			
		}
		return response.getData();
	}

	private Response invokerRemote(Request request) throws IOException {
		Response response = new Response();
		TransportClient client = null;
		try {
			client = selector.select();
			byte[] outBytes = encoder.encoder(request);
			InputStream revice = client.write(new ByteArrayInputStream(outBytes));
			byte[] inBytes = IOUtils.readFully(revice, revice.available());
			response = decoder.decoder(inBytes, Response.class);
			
		} catch (IOException e) {
			log.warn(e.getMessage(), e);
			response.setCode(1);
			response.setMessage("RpcClient got error: " + e.getClass() + " : " + e.getMessage());
			
		}finally {
			if (client != null) {
				selector.release(client);
			}
		}
		return response;
	}

}
